package br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Arc;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.PetriNet;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Place;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Transition;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class PNMLGenerator {

	private Document dom;
	private List<PetriNet> nets;

	public PNMLGenerator(List<PetriNet> nets) {
		this.nets = nets;
		createDocument();
	}

	public void createXML(String filePath) {
		createDOMTree();
		printToFile(filePath);
	}

	private void createDocument() {

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			dom = db.newDocument();
		} catch (ParserConfigurationException pce) {
			System.out
					.println("Error while trying to instantiate DocumentBuilder "
							+ pce);
			System.exit(1);
		}

	}

	private void createDOMTree() {
		Element rootEle = createPNMLElement();
		dom.appendChild(rootEle);
	}

	private Element createPlaceElement(Place plc) {
		Element plcEle = dom.createElement("place");
		plcEle.setAttribute("id", plc.getId());
		plcEle.appendChild(createName(plc.getName(), true));
		plcEle.appendChild(createGraphics("position"));
		plcEle.appendChild(createInitialMarking(plc.getInitialMarking()));
		return plcEle;
	}

	private Element createTransitionElement(Transition t) {
		Element tEle = dom.createElement("transition");
		tEle.setAttribute("id", t.getId());
		tEle.appendChild(createName(t.getName(), true));
		tEle.appendChild(createGraphics("position"));
		return tEle;
	}

	private Element createArcElement(Arc a) {
		Element arcEle = dom.createElement("arc");
		arcEle.setAttribute("id", a.getId());
		arcEle.setAttribute("source", a.getSource().getName());
		arcEle.setAttribute("target", a.getTarget().getName());
		arcEle.appendChild(createInscription(a.getInscription()));
		return arcEle;
	}
	
	private Element createInscription(String text) {
		Element textEle = dom.createElement("text");
		textEle.setTextContent(text);
		Element inscriptionEle = dom.createElement("isncription");
		inscriptionEle.appendChild(textEle);
		return inscriptionEle;
	}
	
	private Element createName(String name, boolean createGraphics) {
		Element textEle = dom.createElement("text");
		textEle.setTextContent(name);
		Element nameEle = dom.createElement("name");
		nameEle.appendChild(textEle);
		if (createGraphics) {
			nameEle.appendChild(createGraphics("offset"));
		}
		return nameEle;
	}
	
	private Element createGraphics(String child) {
		Element childEle = dom.createElement(child);
		childEle.setAttribute("x", "0");
		childEle.setAttribute("y", "0");
		Element graphicsEle = dom.createElement("graphics");
		graphicsEle.appendChild(childEle);
		return graphicsEle;
	}
	
	private Element createInitialMarking(String marking) {
		Element textEle = dom.createElement("text");
		textEle.setTextContent(marking);
		Element markEle = dom.createElement("initialMarking");
		markEle.appendChild(textEle);
		markEle.appendChild(createGraphics("offset"));
		
		return markEle;
	}
	
	private Element createNetElement(PetriNet net) {
		Element netEle = dom.createElement("net");
		netEle.setAttribute("id", net.getId());
		netEle.setAttribute("type", net.getType());
//		netEle.setAttribute("type", "http://www.pnml.org/version-2009/grammar/ptnet");
		
		netEle.appendChild(createName("undefined", false));
		
		Element pageEle = dom.createElement("page");
		pageEle.setAttribute("id", "undefined");
		
		netEle.appendChild(pageEle);
		for (Place plc : net.getPlaces()) {
			Element plcEle = createPlaceElement(plc);
			pageEle.appendChild(plcEle);
		}

		for (Transition t : net.getTransitions()) {
			Element tEle = createTransitionElement(t);
			pageEle.appendChild(tEle);
		}

		for (Arc arc : net.getArcs()) {
			Element arcEle = createArcElement(arc);
			pageEle.appendChild(arcEle);
		}
		return netEle;
	}

	private Element createPNMLElement() {
		Element pnmlEle = dom.createElement("pnml");
		pnmlEle.setAttribute("xmlns", "http://www.pnml.org/version-2009/grammar/pnml");

		for (PetriNet net : nets) {
			Element netEle = createNetElement(net);
			pnmlEle.appendChild(netEle);
		}
		
		return pnmlEle;
	}

	private void printToFile(String filePath) {

		try {
			OutputFormat format = new OutputFormat(dom);
			format.setIndenting(true);

			XMLSerializer serializer = new XMLSerializer(new FileOutputStream(
					new File(filePath)), format);

			serializer.serialize(dom);

		} catch (IOException ie) {
			ie.printStackTrace();
		}
	}
}